package com.example.mq.mqclient;

import com.example.mq.common.*;
import com.example.mq.mqsever.BrokerServer;

import java.io.*;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-09-08
 * Time: 13:38
 */
public class Connection {
    private Socket socket = null;
    //需要管理多个channel,使用一个哈希表组织起来
    private ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();

    private InputStream inputStream;
    private OutputStream outputStream;
    private DataInputStream dataInputStream;
    private DataOutputStream dataOutputStream;
    private ExecutorService callbackPoll=null;

    public Connection(String host, int port) throws IOException {
        socket = new Socket(host, port);
        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();
        dataInputStream = new DataInputStream(inputStream);
        dataOutputStream = new DataOutputStream(outputStream);

        callbackPoll = Executors.newFixedThreadPool(4);
        //创建一个扫描线程,由这个线程不停的从socket中读取响应数据,把这个响应数据再交给对应的channel负责处理
        Thread t = new Thread(() -> {
            try {
                while (!socket.isClosed()) {
                    Response response = readResponse();

                    dispatchResponse(response);
                }
            } catch (SocketException e) {
                System.out.println("[Connection]连接正常断开!");
            } catch (MqException | IOException | ClassNotFoundException e) {
                System.out.println("[Connection]连接异常断开!");
                e.printStackTrace();
            }

        });
        t.start();

    }

    public void close(){
        //关闭Connection释放上述连接
        try{
            callbackPoll.shutdownNow();
            channelMap.clear();
            inputStream.close();
            outputStream.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //使用这个方法来分别处理响应,当前的的响应是一个针对请求的响应,还是服务器推送的消息
    private void dispatchResponse(Response response) throws IOException, MqException, ClassNotFoundException {
        if(response.getType()==0xc){
            //服务器推送的消息
            SubScribeReturns subScribeReturns= (SubScribeReturns) BinaryTool.fromBytes(response.getPayload());
            //根据channelId找到对一个的channel对象
            Channel channel=channelMap.get(subScribeReturns.getChannelId());
            if(channel==null){
                throw new MqException("[Connection]该消息对应的channel在客户端中不存在! channelId="+subScribeReturns.getChannelId());

            }
            //执行该channel对象内部的回调
            callbackPoll.submit(()->{
                try{
                    channel.getConsumer().handlerDelivery(subScribeReturns.getConsumeTag(),subScribeReturns.getBasicProperties()
                    ,subScribeReturns.getBody());

                } catch (IOException | MqException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
            });
        }else{
            //当前响应是针对控制请求的响应
            BasicReturns basicReturns= (BasicReturns) BinaryTool.fromBytes(response.getPayload());
            //把这个响应放到对应的channel的哈希表中
            Channel channel=channelMap.get(basicReturns.getChannelId());
            if(channel==null){
                throw new MqException("[Connection] 该消息对应的channel在客户端中不存在! channelId="+basicReturns.getChannelId());
            }
            channel.putReturns(basicReturns);

        }
    }



    //发送请求
    public void writeRequest(Request request) throws IOException {
        dataOutputStream.writeInt(request.getType());
        dataOutputStream.writeInt(request.getLength());
        dataOutputStream.write(request.getPayload());
        dataOutputStream.flush();
        System.out.println("[Connection] 发送请求! type="+request.getType()+", length="+request.getLength());
    }
    //读取响应
    public Response readResponse() throws IOException {
        Response response=new Response();
        response.setType(dataInputStream.readInt());
        response.setLength(dataInputStream.readInt());
        byte[] payload=new byte[response.getLength()];
        int n=dataInputStream.read(payload);

        if(n!=response.getLength()){
            throw new IOException("读取的响应数据不完整!");
        }
        response.setPayload(payload);
        System.out.println("[Connection] 收到响应! type="+response.getType()+", length="+response.getLength());
        return response;
    }

    //这里需要在connection创建出一个channel
    public Channel createChannel() throws IOException {
        String channelId="C-"+ UUID.randomUUID().toString();
        Channel channel=new Channel(channelId,this);
        //把这个channel对象放到Connection管理channel的哈希表中
        channelMap.put(channelId,channel);
        //同时也需要把创建channel的这个消息告诉服务器
     boolean ok=   channel.createChannel();
        if(!ok){
            //创建失败,所以整个的创建channel失败
            //把刚才已经在哈希表里写的数据删掉
            channelMap.remove(channelId);
            return null;
        }
        return channel;
    }
}
